package com.offcn.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.offcn.entity.Result;
import com.offcn.search.dao.SkuMapper;
import com.offcn.search.pojo.SkuInfo;
import com.offcn.search.service.SkuService;
import com.offcn.sellergood.feign.ItemFeign;
import com.offcn.sellergood.pojo.Item;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.*;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * @Author: zola
 * @Date: 2021/9/11 - 09 - 11 - 22:29
 * @Description: com.offcn.search.service.impl
 * @Version: 1.0
 */
@Service
public class SkuServiceImpl implements SkuService {

    // 注入远程调用接口ItemFeign
    @Autowired
    private ItemFeign itemFeign;

    @Autowired
    private SkuMapper skuMapper;

    // 注入elasticsearch模板对象
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    /**
     * @param: null
     * @return: null
     * @description: 将获取所有的item数据导入进来
     * @analyze: 
     */
    @Override
    public void importSku() {
        // 获取远程调用接口中查询所有审核通过的item
        Result<List<Item>> result = itemFeign.findByStatus("1");
        List<Item> itemList = result.getData();
        // 将获取的数据转换成json格式\
        // String json = JSON.toJSONString(Object object); 将从页面传来的json字符串转为字符串
        String itemsJSON = JSON.toJSONString(itemList);
        // 再将字符串转为对象
        // <T> List<T> parseArray(String text, Class<T> clazz)
        List<SkuInfo> skuInfoList = JSON.parseArray(itemsJSON, SkuInfo.class);

        // skuInfo中注入specMap的值
        for (SkuInfo skuInfo : skuInfoList) {
            Map<String,Object> specMap = JSON.parseObject(skuInfo.getSpec(), Map.class);
            skuInfo.setSpecMap(specMap);
        }
        // <S extends T> Iterable<S> saveAll(Iterable<S> var1);
        skuMapper.saveAll(skuInfoList);
    }




    /**
     * @param: Map<String, String> searchMap=("keywords","手机")，是个json串
     * @return: map
     * @description: 从es中获取数据
     * @analyze:
     */
    @Override
    public Map search(Map<String, String> searchMap) {
        // 获取从页面传来的searchMap中的keywords,并对获取进行搜索的关键词是否是空，如果是空，设定查询的值
        String keywords = searchMap.get("keywords");
        if(StringUtils.isEmpty(keywords)){
            keywords = "手机";
        }
        /*-----------------------------------------获取分页数据-------------------------------------------------------*/
        Integer pageNum = Integer.parseInt(searchMap.get("pageNum"));  // 获取当前显示第num页的数据
        if (pageNum == null){
            pageNum = 1;
        }
        Integer pageSize = 10; // 页容量

        /*-----------------------------------------执行搜索查询-------------------------------------------------------*/
        /*-----------------------------------------一、创建 查询构建对象----------------------------------------------*/
        /*
        * (1)用于建造一个NativeSearchQuery查询对象
        * (2)用于封装查询条件
        * */
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        /*-----------------------------------------二、设置 查询条件--------------------------------------------------*/
        // ----------------------1.条件查询--------------------------------
        // 1.1 分页数据
        queryBuilder.withPageable(PageRequest.of(pageNum,pageSize));

        /*1.QueryBuilders.matchQuery("查询字段","匹配的关键词")*/
        // 查询条件：字段名为：title   内容中含有keywords("手机")所指定的数据
        /*MatchQueryBuilder matchBuilder = QueryBuilders.matchQuery("title", keywords);
        queryBuilder.withQuery(matchBuilder);*/
        // queryBuilder.withQuery(QueryBuilders.matchQuery("title", keywords));

        // 1.2 高亮查询的字段，前缀  后缀
        queryBuilder.withHighlightFields(new HighlightBuilder.Field("title")); // 设置要进行高亮显式的字段
        queryBuilder.withHighlightBuilder(new HighlightBuilder().preTags("<span style=\"color:red\">").postTags("</span>"));

        // 1. 设定搜索的条件 关键字只会和 title 匹配:只设置一个属性
//        queryBuilder.withQuery(QueryBuilders.matchQuery("title",keywords));
//        queryBuilder.withQuery(QueryBuilders.matchPhraseQuery("title",keywords));

        // 1.设定搜索关键词多个字段匹配 ，可以匹配 title，brand，category
        queryBuilder.withQuery(QueryBuilders.multiMatchQuery(keywords,"title","brand","category"));


        // ----------------------2.分组查询--------------------------------
        // ---------(1)分组查询第一步：将指定的字段进行分组后，并添加到查询条件中------
        /*AggregationBuilders.terms("分组识别名").field("分组字段")*/
        // 根据category 字段分组查询：根据字段名："category"进行分组，比指定分组名称
        /*TermsAggregationBuilder termBuilder = AggregationBuilders.terms("skucategory").field("category");
        queryBuilder.addAggregation(termBuilder);*/
        queryBuilder.addAggregation(AggregationBuilders.terms("skuCategory").field("category"));

        // 根据品牌进行分组：根据字段名:"brand"进行分组,并指定分组名称
        queryBuilder.addAggregation(AggregationBuilders.terms("skuBrand").field("brand"));

        // 根据spec进行分组：根据字段名:"skuSpec"进行分组,并指定分组名称
        queryBuilder.addAggregation(AggregationBuilders.terms("skuSpec").field("spec.keyword"));

        // ----------------------3.布尔查询——过滤查询--------------------------------
        /*
        * QueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();   定义过滤查询的构建对象
        * boolQueryBuilder.filter(查询条件);
        * queryBuilder.withFilter(boolQueryBuilder);  将过滤条件和queryBuilder 整合
        * */
        // 根据品牌过滤查询
        if (!StringUtils.isEmpty(searchMap.get("brand"))) {
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            BoolQueryBuilder brandFilter = boolQueryBuilder.filter(QueryBuilders.matchQuery("brand", searchMap.get("brand")));
            queryBuilder.withFilter(brandFilter);
        }

        // 根据分类过滤查询
        if (!StringUtils.isEmpty(searchMap.get("category"))) {
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            BoolQueryBuilder categoryFilter = boolQueryBuilder.filter(QueryBuilders.matchQuery("category", searchMap.get("category")));
            queryBuilder.withFilter(categoryFilter);
        }

        // 根据规格过滤查询：搜索的字段  specMap.规格名.keyword
        //      提交的参数为了方便划分  spec_规格名
        if (searchMap != null) {
            for (String key : searchMap.keySet()) {
                if (key.startsWith("spec_")){  // 规格的查询  spec_网络
                    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
                    String specName = key.substring(5);
                    BoolQueryBuilder specFilter = boolQueryBuilder.filter(QueryBuilders.termQuery("specMap."
                            + specName + ".keyword", searchMap.get(key)));
                    queryBuilder.withFilter(specFilter);
                }
            }
        }

        // 根据价格过滤
        String price = searchMap.get("price");
        if(!StringUtils.isEmpty(price)){
            String[] prices = price.split("-");
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            if (!prices[1].equals("*")){  // 即有上限
                boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").lt(prices[1]));
            }
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(prices[0]));
            queryBuilder.withFilter(boolQueryBuilder);
        }

        // ----------------------3.排序查询--------------------------------
        String sortField = searchMap.get("sortField"); // 排序字段
        String sortRule = searchMap.get("sortRule");// 排序规则：ASC、DESC
        if (!StringUtils.isEmpty(sortField) && !StringUtils.isEmpty(sortRule)) {
            FieldSortBuilder sortBuilder = SortBuilders.fieldSort(sortField)
                    .order(sortRule.equalsIgnoreCase("asc") ? SortOrder.ASC : SortOrder.DESC);
            queryBuilder.withSort(sortBuilder);
        }

        /*-----------------------------------------三、创建 查询对象(构造者模式)----------------------------------------*/
        NativeSearchQuery query = queryBuilder.build();

        /*-----------------------------------------四、执行查询--------------------------------------------------------*/
        /*
        * SearchHits<T> search(Query query, Class<T> clazz)
        * ElasticsearchRestTemplate elasticsearchRestTemplate ：elasticsearchRestTemplate模板对象
        * elasticsearchRestTemplate.search
        * query:构建的搜索对象
        * SkuInfo.class：用于查询结果对应的实体类
        * */
        SearchHits<SkuInfo> searchHits = elasticsearchRestTemplate.search(query, SkuInfo.class);


        // ----------------------(2)分组查询第二步-----------------------------
        // 获取category分组查询结果
        Terms skuCategory = searchHits.getAggregations().get("skuCategory");
        List<String> categoryList = getGroupList(skuCategory);

        // 获取brand分组查询结果
        Terms skuBrand = searchHits.getAggregations().get("skuBrand");
        List<String> brandList = getGroupList(skuBrand);

        // 获取 spec 分组查询结果
        Terms skuSpec = searchHits.getAggregations().get("skuSpec");
        Map<String, Set<String>> specMap = getSpecMap(skuSpec);
        System.out.println(specMap);

        // 对搜索集合进行分页封装:skuPage 包含分页的相关数据 总记录，总页数
        SearchPage<SkuInfo> skuPage = SearchHitSupport.searchPageFor(searchHits, query.getPageable());
        List<SearchHit<SkuInfo>> content = skuPage.getContent();
        List<SkuInfo> skuList = new ArrayList<>();
        for (SearchHit<SkuInfo> skuInfoSearchHit : content) {
            SkuInfo skuInfo = skuInfoSearchHit.getContent();
            skuList.add(skuInfo);
            Map<String, List<String>> highlightFields = skuInfoSearchHit.getHighlightFields();
            for (Map.Entry<String, List<String>> stringListEntry : highlightFields.entrySet()) {
                String key = stringListEntry.getKey();
                if (key.equals("title")){
                    String highLightTitle = stringListEntry.getValue().get(0);
                    skuInfoSearchHit.getContent().setTitle(highLightTitle);
                }
            }
        }
        Map<Object, Object> resultMap = new HashMap<>();
        resultMap.put("rows",skuList);
        resultMap.put("totle",skuPage.getTotalElements());
        resultMap.put("totlePages",skuPage.getTotalPages());
        resultMap.put("categoryList",categoryList);
        resultMap.put("brandList",brandList);
        resultMap.put("specMap",specMap);
        return resultMap;
    }

    // 封装分组查询第二步操作的方法
    private List<String> getGroupList(Terms terms){
        List<String> groups = new ArrayList<>();
        if (terms != null) {
            for (Terms.Bucket bucket : terms.getBuckets()) {
                String brand = bucket.getKeyAsString();
                groups.add(brand);
            }
        }
        return groups;
    }

    // 封装分组方法第二步方法：返回是一个Map<String,Set<String>>类型
    private Map<String, Set<String>> getSpecMap(Terms terms){
        List<String> specList = new ArrayList<>();
        if(terms != null){
            for (Terms.Bucket bucket : terms.getBuckets()) {
                specList.add(bucket.getKeyAsString());
            }
        }
        // 定义满足返回值类型的对象
        Map<String,Set<String>> specResult = new HashMap<>();
        for (String specStr : specList) {
            Map<String, String> map = JSON.parseObject(specStr, Map.class); // String => map<String,String>
            for (Map.Entry<String, String> entry : map.entrySet()) {
                String key = entry.getKey();
                String value = entry.getValue();
                Set<String> set = specResult.get(key);  // 根据key来获取set,看set中是否有元素
                if(set == null){
                    set = new HashSet<>();
                }
                set.add(value);
                specResult.put(key, set);
            }
        }
        return specResult;
    }
}
